Imports System.Drawing

''' <summary>
''' The XAxis object describes the properties of the X Axis including label values and display font.
''' </summary>
''' <remarks></remarks>
Public Class XAxis
    ''' <summary>
    ''' Indicates the alignment of the text on the X-Axis.
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum Rotation As Integer
        Horizontal = 0
        Vertical = 90
        Diagonal = -45
    End Enum

    '---Overall dimensions of chart
    Dim moChartDimensions As ChartDimensions

    '---Appearance
    Protected msAxisName As String
    Protected moAxisFont As Font = New Font(System.Drawing.FontFamily.GenericSansSerif, 10)

    Protected miLabelTopMargin As Integer = 5

    Protected mbShowLines As Boolean = True
    Protected moFont As Font = New Font(System.Drawing.FontFamily.GenericSansSerif, 10)
    Protected moFontColor As Color = Color.Black
    Protected miFontAngle As Rotation = Rotation.Horizontal

    '---Dimensions of X-Axis label area
    Protected miXAxisLabelMaxHeight As Integer = 5

    Protected miXAxisLabelTop As Integer
    Protected miXAxisLabelLeft As Integer

    Protected miXAxisLabelWidth As Integer
    Protected miXAxisLabelHeight As Integer

    Protected miXAxisLabelBottom As Integer
    Protected miXAxisLabelRight As Integer

    Protected moXAxisValues As Single()
    Protected moXAxisLabels As String()

    Protected miXAxisLabelCount As Integer
    Protected mdXAxisLabelBoxWidth As Single

    ''' <summary>
    ''' oChartDimensions is a ChartDimensions object describing the interior of the chart and oXAxisValues() 
    ''' is an array of Single containing a value for each point on the X Axis in left to right order.
    ''' </summary>
    ''' <param name="oChartDimensions"></param>
    ''' <param name="oXAxisValues"></param>
    ''' <remarks></remarks>
    Public Sub New( _
                    ByVal oChartDimensions As ChartDimensions, _
                    ByVal oXAxisValues() As Single)

        moChartDimensions = oChartDimensions
        Me.XAxisValues = oXAxisValues

        ReDim moXAxisLabels(miXAxisLabelCount)
        For i As Short = 0 To miXAxisLabelCount
            moXAxisLabels(i) = oXAxisValues(i)
        Next
    End Sub

    ''' <summary>
    ''' The title of the X Axis
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property AxisName() As String
        Get
            Return msAxisName
        End Get
        Set(ByVal value As String)
            msAxisName = value
        End Set
    End Property

    ''' <summary>
    ''' The System.Drawing.Font object to use when displaying the X Axis title
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property AxisFont() As Font
        Get
            Return moAxisFont
        End Get
        Set(ByVal value As Font)
            moAxisFont = value
        End Set
    End Property

    ''' <summary>
    ''' An array of Single representing X Axis values in left to right order.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property XAxisValues() As Single()
        Get
            Return moXAxisValues
        End Get
        Set(ByVal value As Single())
            moXAxisValues = value

            miXAxisLabelCount = moXAxisValues.GetUpperBound(0)
        End Set
    End Property

    ''' <summary>
    ''' An array of String representing X Axis labels in left to right order. If a value is supplied 
    ''' for this property then the value must have the same number of dimensions as XAxisValues().
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property XAxisLabels() As String()
        Get
            Return moXAxisLabels
        End Get
        Set(ByVal value As String())
            moXAxisLabels = value
        End Set
    End Property

    ''' <summary>
    ''' The System.Drawing.Font object to use when displaying the X Axis labels
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Font() As Font
        Get
            Return moFont
        End Get
        Set(ByVal value As Font)
            moFont = value
        End Set
    End Property

    ''' <summary>
    ''' The rotation of the X Axis labels as a jChart.XAxis.Rotation value.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property FontAngle() As Rotation
        Get
            Return miFontAngle
        End Get
        Set(ByVal value As Rotation)
            miFontAngle = value
        End Set
    End Property

    ''' <summary>
    ''' True if vertical lines should be drawn for each x axis value.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ShowLines() As Boolean
        Get
            Return mbShowLines
        End Get
        Set(ByVal value As Boolean)
            mbShowLines = value
        End Set
    End Property

    Friend Property XAxisLabelCount() As Integer
        Get
            Return miXAxisLabelCount
        End Get
        Set(ByVal value As Integer)
            miXAxisLabelCount = value
        End Set
    End Property

    Friend Property XAxisLabelBoxWidth() As Single
        Get
            Return mdXAxisLabelBoxWidth
        End Get
        Set(ByVal value As Single)
            mdXAxisLabelBoxWidth = value
        End Set
    End Property

    ''' <summary>
    ''' A ChartDimensions object describing the interior of the chart.
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property ChartDimensions() As ChartDimensions
        Get
            Return moChartDimensions
        End Get
        Set(ByVal value As ChartDimensions)
            moChartDimensions = value
        End Set
    End Property

    Friend Sub RecalculateDimensions(ByVal g As Graphics)
        Dim oTitleSize As SizeF = g.MeasureString(msAxisName, moAxisFont)

        miXAxisLabelMaxHeight = 0

        For iCurrentLabel As Integer = 0 To miXAxisLabelCount
            Dim oLabelSize As SizeF = g.MeasureString(moXAxisLabels(iCurrentLabel), moFont)

            If miFontAngle = Rotation.Horizontal Then
                If miXAxisLabelMaxHeight < oLabelSize.Height Then
                    miXAxisLabelMaxHeight = oLabelSize.Height
                End If

            Else
                If miXAxisLabelMaxHeight < oLabelSize.Width Then
                    miXAxisLabelMaxHeight = oLabelSize.Width
                End If
            End If
        Next

        If moChartDimensions.FrameWidthBottom < miXAxisLabelMaxHeight + oTitleSize.Height Then
            moChartDimensions.FrameWidthBottom = miXAxisLabelMaxHeight + oTitleSize.Height
        End If

        CalculateAxisDimensions()
    End Sub

    Private Sub CalculateAxisDimensions()
        '---X Axis area dimensions
        miXAxisLabelTop = moChartDimensions.InnerBottom + miLabelTopMargin
        miXAxisLabelLeft = moChartDimensions.InnerLeft

        miXAxisLabelWidth = moChartDimensions.InnerWidth
        miXAxisLabelHeight = moChartDimensions.XAxisLabelRowHeight

        miXAxisLabelBottom = moChartDimensions.OuterHeight - moChartDimensions.FrameWidthBottom
        miXAxisLabelRight = miXAxisLabelLeft + miXAxisLabelWidth

        mdXAxisLabelBoxWidth = moChartDimensions.InnerWidth / (miXAxisLabelCount + 1)
    End Sub

    Friend Sub DrawXAxisLabelArea(ByRef g As Graphics)
        CalculateAxisDimensions()

        Dim oBrush As New SolidBrush(moChartDimensions.XAxisBackgroundColor)
        Dim oRect As New Rectangle(miXAxisLabelLeft, miXAxisLabelTop, miXAxisLabelWidth, miXAxisLabelHeight)

        g.FillRectangle(oBrush, oRect)

        DrawXAxisValues(g)
    End Sub

    Friend Function CalculateXPos(ByVal iXPos As Integer) As Integer
        Dim iAdjustedXPos As Integer = Math.Round(moChartDimensions.InnerLeft + mdXAxisLabelBoxWidth / 2 + iXPos * mdXAxisLabelBoxWidth)

        Return iAdjustedXPos
    End Function

    Friend Function CalculateXPos(ByVal snXPos As Single) As Integer
        Dim iMinValue As Single = moXAxisValues(0)
        Dim iMaxValue As Single = moXAxisValues(moXAxisValues.GetUpperBound(0))

        Dim snXRange As Single = iMaxValue - iMinValue

        Dim snPercent As Single = 0
        If snXRange > 0 Then snPercent = (snXPos - iMinValue) / snXRange

        Dim iXPos As Integer = Math.Round(moChartDimensions.InnerLeft + (moChartDimensions.InnerWidth * snPercent))

        Return iXPos
    End Function

    Private Sub DrawXAxisValues(ByRef g As Graphics)
        If mbShowLines = True Then
            DrawXAxisLines(g, miXAxisLabelCount, mdXAxisLabelBoxWidth)
        End If

        DrawXAxisLabels(g)
    End Sub

    Private Sub DrawXAxisLines( _
                    ByRef g As Graphics, _
                    ByVal iAxisLabelCount As Integer, _
                    ByVal dLabelBoxWidth As Single)

        For iXPos As Integer = 0 To iAxisLabelCount
            Dim iLineXPos As Integer = moChartDimensions.InnerLeft + dLabelBoxWidth * iXPos
            g.DrawLine(Pens.Blue, iLineXPos, moChartDimensions.InnerTop, iLineXPos, moChartDimensions.InnerBottom)
        Next

        g.DrawLine(New Pen(Color.Blue, 1), moChartDimensions.InnerRight, moChartDimensions.InnerTop, moChartDimensions.InnerRight, moChartDimensions.InnerBottom)
    End Sub

    Private Sub DrawXAxisLabels( _
            ByRef g As Graphics)

        Dim oBrush As New SolidBrush(moFontColor)

        Dim sLabel As String

        For iCurrentLabel As Integer = 0 To miXAxisLabelCount
            If Not moXAxisLabels Is Nothing Then
                sLabel = moXAxisLabels(iCurrentLabel)
            Else
                sLabel = moXAxisValues(iCurrentLabel)
            End If

            Dim iTextWidth As Integer = g.MeasureString(sLabel, moFont).Width
            Dim iTextHeight As Integer = g.MeasureString(sLabel, moFont).Height

            If miFontAngle = Rotation.Diagonal Then
                Dim iXPosMin As Integer = miXAxisLabelLeft + (iCurrentLabel * mdXAxisLabelBoxWidth) + (mdXAxisLabelBoxWidth / 2)

                g.TranslateTransform(iXPosMin, miXAxisLabelTop)
                g.RotateTransform(miFontAngle)
                g.DrawString(sLabel, moFont, oBrush, -1 * iTextWidth, 0)
                g.ResetTransform()

            ElseIf miFontAngle = Rotation.Horizontal Then
                Dim iXPosMin As Integer = miXAxisLabelLeft + (iCurrentLabel * mdXAxisLabelBoxWidth) + (mdXAxisLabelBoxWidth / 2) - (iTextHeight / 2)

                g.DrawString(sLabel, moFont, oBrush, iXPosMin, miXAxisLabelTop)

            ElseIf miFontAngle = Rotation.Vertical Then
                Dim iXPosMin As Integer = miXAxisLabelLeft + (iCurrentLabel * mdXAxisLabelBoxWidth) + (mdXAxisLabelBoxWidth / 2) - (iTextHeight / 2)

                Dim oSF As New System.Drawing.StringFormat(System.Drawing.StringFormatFlags.DirectionVertical)
                g.DrawString(sLabel, moFont, oBrush, iXPosMin, miXAxisLabelTop, oSF)
            End If
        Next

        Dim oSize As SizeF = g.MeasureString(msAxisName, moAxisFont)
        g.DrawString( _
                msAxisName, _
                moAxisFont, _
                New SolidBrush(moFontColor), _
                CInt(miXAxisLabelLeft + (moChartDimensions.InnerWidth - oSize.Width) / 2), _
                miXAxisLabelTop + miXAxisLabelMaxHeight)

    End Sub

End Class
